/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the Apache License, Version 2.0  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

/*! \file pgw_pcef_emulation.c
  \brief
  \author Lionel Gauthier
  \company Eurecom
  \email: lionel.gauthier@eurecom.fr
*/
#define SGW

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <netinet/in.h>

#include "bstrlib.h"

#include "dynamic_memory_check.h"
#include "assertions.h"
#include "conversions.h"
#include "hashtable.h"
#include "obj_hashtable.h"
#include "common_defs.h"
#include "intertask_interface.h"
#include "msc.h"
#include "log.h"
#include "3gpp_24.008.h"
#include "spgw_config.h"
#include "pgw_pcef_emulation.h"
#include "sgw_context_manager.h"
#include "pgw_procedures.h"
#include "sgw.h"
#include "async_system.h"

#ifdef __cplusplus
extern "C" {
#endif

extern pgw_app_t                        pgw_app;

static void free_pcc_rule (void ** rule);

//------------------------------------------------------------------------------
int pgw_pcef_emulation_init (const pgw_config_t * const pgw_config_p)
{
  int            rc = RETURNok;
  hashtable_rc_t hrc = HASH_TABLE_OK;

  //--------------------------
  // Predefined PCC rules
  //--------------------------
  pgw_app.deactivated_predefined_pcc_rules = hashtable_ts_create (32, NULL, free_pcc_rule, NULL);


  pcc_rule_t * pcc_rule = calloc (1, sizeof (pcc_rule_t));
  pcc_rule->name = bfromcstr("VOLTE_40K_PCC_RULE");
  pcc_rule->is_activated = false;
  pcc_rule->sdf_id  = SDF_ID_GBR_VOLTE_40K;
  pcc_rule->bearer_qos.pci = PRE_EMPTION_CAPABILITY_ENABLED;
  pcc_rule->bearer_qos.pl  = 2;
  pcc_rule->bearer_qos.pvi = PRE_EMPTION_VULNERABILITY_DISABLED;
  pcc_rule->bearer_qos.qci = 1;
  pcc_rule->bearer_qos.gbr.br_ul = 40; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->bearer_qos.gbr.br_dl = 40; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->bearer_qos.mbr.br_ul = 40; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->bearer_qos.mbr.br_dl = 40; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->sdf_template.sdf_filter[0].identifier = PF_ID_VOLTE;
  pcc_rule->sdf_template.sdf_filter[0].spare = 0;
  pcc_rule->sdf_template.sdf_filter[0].direction = TRAFFIC_FLOW_TEMPLATE_BIDIRECTIONAL;
  pcc_rule->sdf_template.sdf_filter[0].eval_precedence = 2;
  pcc_rule->sdf_template.sdf_filter[0].length = 9;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.flags = TRAFFIC_FLOW_TEMPLATE_IPV4_REMOTE_ADDR_FLAG;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[0].addr = 216;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[1].addr = 58;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[2].addr = 210;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[3].addr = 212;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[0].mask = 255;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[1].mask = 255;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[2].mask = 255;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[3].mask = 255;
  pcc_rule->sdf_template.number_of_packet_filters = 1;
  hrc = hashtable_ts_insert(pgw_app.deactivated_predefined_pcc_rules, pcc_rule->sdf_id, pcc_rule);
  if (HASH_TABLE_OK != hrc) {
    return RETURNerror;
  }


  pcc_rule = calloc (1, sizeof (pcc_rule_t));
  pcc_rule->name = bfromcstr("VOLTE_64K_PCC_RULE");
  pcc_rule->is_activated = false;
  pcc_rule->sdf_id  = SDF_ID_GBR_VOLTE_64K;
  pcc_rule->bearer_qos.pci = PRE_EMPTION_CAPABILITY_ENABLED;
  pcc_rule->bearer_qos.pl  = 2;
  pcc_rule->bearer_qos.pvi = PRE_EMPTION_VULNERABILITY_DISABLED;
  pcc_rule->bearer_qos.qci = 1;
  pcc_rule->bearer_qos.gbr.br_ul = 64; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->bearer_qos.gbr.br_dl = 64; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->bearer_qos.mbr.br_ul = 64; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->bearer_qos.mbr.br_dl = 64; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->sdf_template.sdf_filter[0].identifier = PF_ID_VOLTE;
  pcc_rule->sdf_template.sdf_filter[0].spare = 0;
  pcc_rule->sdf_template.sdf_filter[0].direction = TRAFFIC_FLOW_TEMPLATE_BIDIRECTIONAL;
  pcc_rule->sdf_template.sdf_filter[0].eval_precedence = 2;
  pcc_rule->sdf_template.sdf_filter[0].length = 9;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.flags = TRAFFIC_FLOW_TEMPLATE_IPV4_REMOTE_ADDR_FLAG;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[0].addr = 216;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[1].addr = 58;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[2].addr = 210;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[3].addr = 212;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[0].mask = 255;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[1].mask = 255;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[2].mask = 255;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[3].mask = 255;
  pcc_rule->sdf_template.number_of_packet_filters = 1;
  hrc = hashtable_ts_insert(pgw_app.deactivated_predefined_pcc_rules, pcc_rule->sdf_id, pcc_rule);
  if (HASH_TABLE_OK != hrc) {
    return RETURNerror;
  }


  pcc_rule = calloc (1, sizeof (pcc_rule_t));
  pcc_rule->name = bfromcstr("VILTE_192K_PCC_RULE");
  pcc_rule->is_activated = false;
  pcc_rule->sdf_id  = SDF_ID_GBR_VILTE_192K;
  pcc_rule->bearer_qos.pci = PRE_EMPTION_CAPABILITY_ENABLED;
  pcc_rule->bearer_qos.pl  = 2;
  pcc_rule->bearer_qos.pvi = PRE_EMPTION_VULNERABILITY_DISABLED;
  pcc_rule->bearer_qos.qci = 2;
  pcc_rule->bearer_qos.gbr.br_ul = 192; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->bearer_qos.gbr.br_dl = 192; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->bearer_qos.mbr.br_ul = 192; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->bearer_qos.mbr.br_dl = 192; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->sdf_template.sdf_filter[0].identifier = PF_ID_VILTE;
  pcc_rule->sdf_template.sdf_filter[0].spare = 0;
  pcc_rule->sdf_template.sdf_filter[0].direction = TRAFFIC_FLOW_TEMPLATE_BIDIRECTIONAL;
  pcc_rule->sdf_template.sdf_filter[0].eval_precedence = 2;
  pcc_rule->sdf_template.sdf_filter[0].length = 9;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.flags = TRAFFIC_FLOW_TEMPLATE_IPV4_REMOTE_ADDR_FLAG;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[0].addr = 216;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[1].addr = 58;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[2].addr = 210;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[3].addr = 213;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[0].mask = 255;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[1].mask = 255;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[2].mask = 255;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[3].mask = 255;
  pcc_rule->sdf_template.number_of_packet_filters = 1;
  hrc = hashtable_ts_insert(pgw_app.deactivated_predefined_pcc_rules, pcc_rule->sdf_id, pcc_rule);
  if (HASH_TABLE_OK != hrc) {
    return RETURNerror;
  }


  pcc_rule = calloc (1, sizeof (pcc_rule_t));
  pcc_rule->name = bfromcstr("TEST_PING_PCC_RULE");
  pcc_rule->is_activated = false;
  pcc_rule->sdf_id  = SDF_ID_TEST_PING;
  pcc_rule->bearer_qos.pci = PRE_EMPTION_CAPABILITY_DISABLED;
  pcc_rule->bearer_qos.pl  = 15;
  pcc_rule->bearer_qos.pvi = PRE_EMPTION_VULNERABILITY_ENABLED;
  pcc_rule->bearer_qos.qci = 7;
  pcc_rule->bearer_qos.gbr.br_ul = 0; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->bearer_qos.gbr.br_dl = 0; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->bearer_qos.mbr.br_ul = 8; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->bearer_qos.mbr.br_dl = 8; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->sdf_template.sdf_filter[0].identifier = PF_ID_PING;
  pcc_rule->sdf_template.sdf_filter[0].spare = 0;
  pcc_rule->sdf_template.sdf_filter[0].direction = TRAFFIC_FLOW_TEMPLATE_BIDIRECTIONAL;
  pcc_rule->sdf_template.sdf_filter[0].eval_precedence = 2;
  pcc_rule->sdf_template.sdf_filter[0].length = 9;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.flags = TRAFFIC_FLOW_TEMPLATE_PROTOCOL_NEXT_HEADER_FLAG;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.protocolidentifier_nextheader = IPPROTO_ICMP;
  pcc_rule->sdf_template.number_of_packet_filters = 1;
  hrc = hashtable_ts_insert(pgw_app.deactivated_predefined_pcc_rules, pcc_rule->sdf_id, pcc_rule);
  if (HASH_TABLE_OK != hrc) {
    return RETURNerror;
  }


  // really necessary ?
  pcc_rule = calloc (1, sizeof (pcc_rule_t));
  pcc_rule->name = bfromcstr("DEFAULT_PCC_RULE");
  pcc_rule->is_activated = false;
  pcc_rule->sdf_id = SDF_ID_NGBR_DEFAULT;
  pcc_rule->bearer_qos.pci = PRE_EMPTION_CAPABILITY_DISABLED;
  pcc_rule->bearer_qos.pl  = 15;
  pcc_rule->bearer_qos.pvi = PRE_EMPTION_VULNERABILITY_ENABLED;
  pcc_rule->bearer_qos.qci = 9;
  pcc_rule->bearer_qos.gbr.br_ul = 0; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->bearer_qos.gbr.br_dl = 0; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->bearer_qos.mbr.br_ul = 1000; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->bearer_qos.mbr.br_dl = 1000; // kilobits per second (1 kbps = 1000 bps)
  pcc_rule->sdf_template.sdf_filter[0].identifier = PF_ID_DEFAULT;
  pcc_rule->sdf_template.sdf_filter[0].spare = 0;
  pcc_rule->sdf_template.sdf_filter[0].direction = TRAFFIC_FLOW_TEMPLATE_DOWNLINK_ONLY;
  pcc_rule->sdf_template.sdf_filter[0].eval_precedence = 250;
  pcc_rule->sdf_template.sdf_filter[0].length = 9;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.flags = TRAFFIC_FLOW_TEMPLATE_IPV4_LOCAL_ADDR_FLAG;
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[0].addr = (uint8_t) ((pgw_config_p->ue_pool_network[0].s_addr) & 0x000000FF);
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[1].addr = (uint8_t) ((pgw_config_p->ue_pool_network[0].s_addr >> 8) & 0x000000FF);
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[2].addr = (uint8_t) ((pgw_config_p->ue_pool_network[0].s_addr >> 16) & 0x000000FF);
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[3].addr = (uint8_t) ((pgw_config_p->ue_pool_network[0].s_addr >> 24) & 0x000000FF);

  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[0].mask = (uint8_t) ((pgw_config_p->ue_pool_netmask[0].s_addr) & 0x000000FF);
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[1].mask = (uint8_t) ((pgw_config_p->ue_pool_netmask[0].s_addr >> 8) & 0x000000FF);
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[2].mask = (uint8_t) ((pgw_config_p->ue_pool_netmask[0].s_addr >> 16) & 0x000000FF);
  pcc_rule->sdf_template.sdf_filter[0].packetfiltercontents.ipv4remoteaddr[3].mask = (uint8_t) ((pgw_config_p->ue_pool_netmask[0].s_addr >> 24) & 0x000000FF);
  pcc_rule->sdf_template.number_of_packet_filters = 1;
  hrc = hashtable_ts_insert(pgw_app.deactivated_predefined_pcc_rules, pcc_rule->sdf_id, pcc_rule);
  if (HASH_TABLE_OK != hrc) {
    return RETURNerror;
  }

  for (int i = 0; i < (SDF_ID_MAX-1); i++) {
    if (pgw_config_p->pcef.preload_static_sdf_identifiers[i]) {
      pgw_pcef_emulation_apply_rule(pgw_config_p->pcef.preload_static_sdf_identifiers[i], pgw_config_p);
    } else
        break;
  }

  if (pgw_config_p->pcef.automatic_push_dedicated_bearer_sdf_identifier) {
    pgw_pcef_emulation_apply_rule(pgw_config_p->pcef.automatic_push_dedicated_bearer_sdf_identifier, pgw_config_p);
  }

  return rc;
}

//------------------------------------------------------------------------------
void pgw_pcef_emulation_exit (void)
{
  if (pgw_app.deactivated_predefined_pcc_rules) {
    hashtable_ts_destroy (pgw_app.deactivated_predefined_pcc_rules);
  }
}

//------------------------------------------------------------------------------
// may change sdf_id to PCC_rule name ?
void pgw_pcef_emulation_apply_rule(const sdf_id_t sdf_id, const pgw_config_t * const pgw_config_p)
{
  pcc_rule_t         *pcc_rule = NULL;
  hashtable_rc_t hrc = hashtable_ts_get(pgw_app.deactivated_predefined_pcc_rules, sdf_id, (void**)&pcc_rule);

  if (HASH_TABLE_OK == hrc) {
    if (!pcc_rule->is_activated) {
      OAILOG_INFO (LOG_SPGW_APP, "Loading PCC rule %s\n", bdata(pcc_rule->name));
      pcc_rule->is_activated = true;
      for (int sdff_i = 0; sdff_i < pcc_rule->sdf_template.number_of_packet_filters; sdff_i++) {
        pgw_pcef_emulation_apply_sdf_filter(&pcc_rule->sdf_template.sdf_filter[sdff_i], pcc_rule->sdf_id, pgw_config_p);
      }
    }
  }
}

//------------------------------------------------------------------------------
void pgw_pcef_emulation_apply_sdf_filter(sdf_filter_t   * const sdf_f, const sdf_id_t sdf_id, const pgw_config_t * const pgw_config_p)
{
  if ((TRAFFIC_FLOW_TEMPLATE_BIDIRECTIONAL == sdf_f->direction)  || (TRAFFIC_FLOW_TEMPLATE_DOWNLINK_ONLY == sdf_f->direction)) {
    bstring filter = pgw_pcef_emulation_packet_filter_2_iptable_string(&sdf_f->packetfiltercontents, sdf_f->direction);

    bstring marking_command = NULL;
    if ((TRAFFIC_FLOW_TEMPLATE_IPV4_REMOTE_ADDR_FLAG | TRAFFIC_FLOW_TEMPLATE_IPV6_REMOTE_ADDR_FLAG) & sdf_f->packetfiltercontents.flags) {
      marking_command = bformat("iptables -I POSTROUTING -t mangle  %s -j MARK --set-mark %d",  bdata(filter), sdf_id);
    } else {
      //marking_command = bformat("iptables -I PREROUTING -t mangle --in-interface %s --dest %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"/%"PRIu8" %s -j MARK --set-mark %d",
      marking_command = bformat("iptables -I POSTROUTING -t mangle  -m iprange --dst-range %s-%s %s -j MARK --set-mark %d",
          inet_ntoa(pgw_config_p->ue_pool_range_low[0]),
          inet_ntoa(pgw_config_p->ue_pool_range_high[0]), bdata(filter), sdf_id);
    }
    bdestroy_wrapper(&filter);
    async_system_command (TASK_ASYNC_SYSTEM, false, bdata(marking_command));
    bdestroy_wrapper(&marking_command);

    // for UE <-> PGW traffic
    filter = pgw_pcef_emulation_packet_filter_2_iptable_string(&sdf_f->packetfiltercontents, sdf_f->direction);

    marking_command = NULL;
    if ((TRAFFIC_FLOW_TEMPLATE_IPV4_REMOTE_ADDR_FLAG | TRAFFIC_FLOW_TEMPLATE_IPV6_REMOTE_ADDR_FLAG) & sdf_f->packetfiltercontents.flags) {
      marking_command = bformat("iptables -I OUTPUT -t mangle  %s -j MARK --set-mark %d",  bdata(filter), sdf_id);
    } else {
      marking_command = bformat("iptables -I OUTPUT -t mangle  -m iprange --dst-range %s-%s %s -j MARK --set-mark %d",
          inet_ntoa(pgw_config_p->ue_pool_range_low[0]),
          inet_ntoa(pgw_config_p->ue_pool_range_high[0]), bdata(filter), sdf_id);
    }
    bdestroy_wrapper(&filter);
    async_system_command (TASK_ASYNC_SYSTEM, false, bdata(marking_command));
    bdestroy_wrapper(&marking_command);


  }
}

//------------------------------------------------------------------------------
bstring pgw_pcef_emulation_packet_filter_2_iptable_string(packet_filter_contents_t * const packetfiltercontents, uint8_t direction)
{
  bstring bstr = bfromcstralloc(64, " ");

  if ((TRAFFIC_FLOW_TEMPLATE_DOWNLINK_ONLY == direction) || (TRAFFIC_FLOW_TEMPLATE_BIDIRECTIONAL == direction)){
    if (TRAFFIC_FLOW_TEMPLATE_IPV4_REMOTE_ADDR_FLAG & packetfiltercontents->flags) {
      bformata(bstr, "  --destination %d.%d.%d.%d/%d.%d.%d.%d",
        packetfiltercontents->ipv4remoteaddr[0].addr, packetfiltercontents->ipv4remoteaddr[1].addr,
        packetfiltercontents->ipv4remoteaddr[2].addr, packetfiltercontents->ipv4remoteaddr[3].addr,
        packetfiltercontents->ipv4remoteaddr[0].mask, packetfiltercontents->ipv4remoteaddr[1].mask,
        packetfiltercontents->ipv4remoteaddr[2].mask, packetfiltercontents->ipv4remoteaddr[3].mask);
    } else {
      bformata(bstr, " --source %d.%d.%d.%d/%d.%d.%d.%d",
        packetfiltercontents->ipv4remoteaddr[0].addr, packetfiltercontents->ipv4remoteaddr[1].addr,
        packetfiltercontents->ipv4remoteaddr[2].addr, packetfiltercontents->ipv4remoteaddr[3].addr,
        packetfiltercontents->ipv4remoteaddr[0].mask, packetfiltercontents->ipv4remoteaddr[1].mask,
        packetfiltercontents->ipv4remoteaddr[2].mask, packetfiltercontents->ipv4remoteaddr[3].mask);
    }
  }
  if (TRAFFIC_FLOW_TEMPLATE_IPV6_REMOTE_ADDR_FLAG & packetfiltercontents->flags) {
    AssertFatal(0, "TODO"); // we have time
  }
  if (TRAFFIC_FLOW_TEMPLATE_PROTOCOL_NEXT_HEADER_FLAG & packetfiltercontents->flags) {
    bformata(bstr, " --protocol %u", packetfiltercontents->protocolidentifier_nextheader);
  }
  if (TRAFFIC_FLOW_TEMPLATE_SINGLE_LOCAL_PORT_FLAG & packetfiltercontents->flags) {
    if ((TRAFFIC_FLOW_TEMPLATE_DOWNLINK_ONLY == direction) || (TRAFFIC_FLOW_TEMPLATE_BIDIRECTIONAL == direction)){
      bformata(bstr, " --destination-port %" PRIu16" ", packetfiltercontents->singlelocalport);
    } else if (TRAFFIC_FLOW_TEMPLATE_UPLINK_ONLY == direction) {
      bformata(bstr, " --source-port %" PRIu16" ", packetfiltercontents->singlelocalport);
    }
  }
  if (TRAFFIC_FLOW_TEMPLATE_LOCAL_PORT_RANGE_FLAG & packetfiltercontents->flags) {
    AssertFatal(0, "TODO LOCAL_PORT_RANGE");
  }
  if (TRAFFIC_FLOW_TEMPLATE_SINGLE_REMOTE_PORT_FLAG & packetfiltercontents->flags) {
    if ((TRAFFIC_FLOW_TEMPLATE_DOWNLINK_ONLY == direction) || (TRAFFIC_FLOW_TEMPLATE_BIDIRECTIONAL == direction)){
      bformata(bstr, " --source-port %" PRIu16" ", packetfiltercontents->singleremoteport);
    } else if (TRAFFIC_FLOW_TEMPLATE_UPLINK_ONLY == direction) {
      bformata(bstr, " --destination-port %" PRIu16" ", packetfiltercontents->singleremoteport);
    }
  }
  if (TRAFFIC_FLOW_TEMPLATE_REMOTE_PORT_RANGE_FLAG & packetfiltercontents->flags) {
    AssertFatal(0, "TODO REMOTE_PORT_RANGE");
  }
  if (TRAFFIC_FLOW_TEMPLATE_SECURITY_PARAMETER_INDEX_FLAG & packetfiltercontents->flags) {
    bformata(bstr, " -m esp --espspi %" PRIu32" ", packetfiltercontents->securityparameterindex);
  }
  if (TRAFFIC_FLOW_TEMPLATE_TYPE_OF_SERVICE_TRAFFIC_CLASS_FLAG & packetfiltercontents->flags) {
    // TODO mask
    bformata(bstr, " -m tos --tos 0x%02X", packetfiltercontents->typdeofservice_trafficclass.value);
  }
  if (TRAFFIC_FLOW_TEMPLATE_FLOW_LABEL_FLAG & packetfiltercontents->flags) {
    AssertFatal(0, "TODO"); // we have time
  }
  return bstr;
}

//------------------------------------------------------------------------------
int pgw_pcef_get_sdf_parameters (const sdf_id_t sdf_id, bearer_qos_t * const bearer_qos, packet_filter_t * const packet_filter, uint8_t * const num_pf)
{
  pcc_rule_t         *pcc_rule = NULL;
  hashtable_rc_t hrc = hashtable_ts_get(pgw_app.deactivated_predefined_pcc_rules, sdf_id, (void**)&pcc_rule);

  if (HASH_TABLE_OK == hrc) {
    if (pcc_rule->is_activated) {
      memcpy(bearer_qos, &pcc_rule->bearer_qos, sizeof(pcc_rule->bearer_qos));
      memcpy(packet_filter, &pcc_rule->sdf_template.sdf_filter, sizeof(pcc_rule->sdf_template.sdf_filter[0]) * pcc_rule->sdf_template.number_of_packet_filters);
     *num_pf = pcc_rule->sdf_template.number_of_packet_filters;
      return RETURNok;
    }
  }
  memset(bearer_qos, 0, sizeof(*bearer_qos));
  memset(packet_filter, 0, sizeof(*packet_filter));
  *num_pf = 0;
  return RETURNerror;
}

//------------------------------------------------------------------------------
static void free_pcc_rule (void ** rule)
{
  if (rule) {
    pcc_rule_t   *pcc_rule = (pcc_rule_t*)*rule;
    if (pcc_rule) {
      if (pcc_rule->name) {
        bdestroy_wrapper(&pcc_rule->name);
      }
      free_wrapper(rule);
    }
  }
}

//------------------------------------------------------------------------------
pcc_rule_t* pgw_pcef_get_rule_by_id(const sdf_id_t sdf_id)
{
  pcc_rule_t* rule = NULL;
  //hashtable_rc_t hrc =
  hashtable_ts_get(pgw_app.deactivated_predefined_pcc_rules, sdf_id, (void**)&rule);
  return rule;
}

#ifdef __cplusplus
}
#endif
